package de.hwrberlin.remo.algorithm.forwardchaining;

import java.util.ArrayList;
import java.util.List;

import de.hwrberlin.remo.algorithm.AlgorithmInitializer;
import de.hwrberlin.remo.algorithm.InferenzAlgorithm;
import de.hwrberlin.remo.algorithm.steps.SetStateStep;
import de.hwrberlin.remo.algorithm.steps.ValidateRuleStep;
import de.hwrberlin.remo.model.KnowledgeBase;
import de.hwrberlin.remo.model.KnowledgeBaseElement;
import de.hwrberlin.remo.model.Rule;
import de.hwrberlin.remo.model.LinksFromRules;

/**
 * This class implements the forward chaining algorithm, which
 * uses a tree to represent all rules (and facts) of
 * the knowledge base.
 * 
 * @author Mario S��nger (IT 09)
 * @version 1.0
 *
 */
public class TreeBasedForwardChaining extends InferenzAlgorithm {

	@Override
	public void run(KnowledgeBase base) {
		List<KnowledgeBaseElement> knownElements = new ArrayList<KnowledgeBaseElement>();
		List<Rule> unpredictableRules = new ArrayList<Rule>();
		
		knownElements.addAll(base.getFacts());
		unpredictableRules.addAll(base.getRules());
		
		// Run until no rules are unpredictable 
		while(unpredictableRules.size() > 0) {
			List<Rule> predictableRules = getPredictableRules(knownElements, unpredictableRules);
			
			unpredictableRules.removeAll(predictableRules);
			knownElements.addAll(predictableRules);
			
			// Validate all predictable rules
			for(Rule rule : predictableRules) {
				ValidateRuleStep validateRule = new ValidateRuleStep(rule, getUserInterface(), editingHelper);
				validateRule.perform();
				
				fireStepFinishedEvent(validateRule);
				
				SetStateStep setStep = new SetStateStep(getEditingHelper(), rule, rule.checkRule());
				setStep.perform();
				
				fireStepFinishedEvent(setStep);
			}
		}
	}
	
	private List<Rule> getPredictableRules(
			List<KnowledgeBaseElement> knownElements, List<Rule> rules) {
		
		List<Rule> resultRules = new ArrayList<Rule>();
		boolean allPremisesPresent;
		
		for(Rule rule : rules) {
			allPremisesPresent = true;
			
			for (LinksFromRules premise : rule.getLinksrules()) {
				if(! knownElements.contains(premise.getElement())) {
					allPremisesPresent = false;
					break;
				}
			}
			
			if(allPremisesPresent) {
				resultRules.add(rule);
			}
		}
		
		return resultRules;
	}

	@Override
	public AlgorithmInitializer getInitializer() {
		// No initializer is needed
		return null;
	}
}